#include "StdAfx.h"
#include <MyFC/ListControl.h>

namespace My {

// Row

Row::Row() :
	m_pList(0), m_Index(-1), m_Visible(true)
{
}

CRect Row::Bounds() const
{
	Span bounds = RangeItem::GetBounds();
	if (GetList() == 0)
		return CRect(0, bounds.first, 0, bounds.last);
	else {
		CRect r = GetList()->ClientRect();
		r.top = bounds.first;
		r.bottom = bounds.last;
		return r;
	}
}

int Row::GetVisibleIndex() const
{
	return m_Visible ? int(RangeItem::GetIndex()) : -1;
}

void Row::SetVisible(bool on)
{
	if (m_Visible != on) {
		if (GetList() != 0)
			GetList()->SetRowVisible(* this, on);
		else
			m_Visible = on;
	}
}

void Row::SetRowHeight(My::Size s) 
{
	if (s != GetRowHeight()) {
		RangeItem::Resize(s);
		if (m_pList != 0)
			Invalidate();
	}
}

My::Size Row::CalcVisibleIndex() const
{
	if (GetList() == 0)
		return 0;
	if (RangeItem::GetIndex() != -1)
		return My::Size(RangeItem::GetIndex());
	else {
		assert(m_Index != -1);
		// search for the next visible row
		for (My::Size i = My::Size(m_Index + 1); i < GetList()->GetRowCount(); ++i) {
			int index = GetList()->GetRow(i).GetVisibleIndex();
			if (index != -1)
				return My::Size(index);
		}
		// if none, then it should be the last
		return GetList()->GetVisibleRowCount();
	}
}

void Row::BoundsChanged()
{
	UpdateBounds();
}


// List

List::List(ScrollBox & box) :
	m_Box(box)
{
	m_Box.Add(this);
	m_AutoScroll.SetDelay(100);
	m_AutoScroll.Attach(this);
}

bool List::IsEmpty() const
{
	return m_Rows.empty();
}

My::Size List::GetRowCount() const
{
	return m_Rows.size();
}

Row & List::GetRow(My::Size i) const
{
	assert(i < m_Rows.size());
	return * m_Rows[i];
}

My::Size List::GetVisibleRowCount() const
{
	return Range::GetItemCount();
}

Row & List::GetVisibleRow(My::Size i) const
{
	return static_cast<Row &>(Range::GetItem(i));
}

My::Size List::GetTotalHeight() const
{
	return Range::GetSize() + 10;
}

void List::Insert(My::Size pos, Row * rows[], My::Size count)
{
	if (count == 0)
		return;
	assert(pos <= m_Rows.size());
	m_Rows.insert(m_Rows.begin() + pos, rows, rows + count);
	std::vector<RangeItem *> visible;
	visible.reserve(count);
	for (My::Size i = pos; i < pos + count; ++i) {
		Row * pRow = m_Rows[i];
		Container::Add(pRow);
		pRow->m_pList = this;
		pRow->m_Index = int(i);
		if (pRow->m_Visible)
			visible.push_back(pRow);
	}
	for (; i < m_Rows.size(); ++i)
		m_Rows[i]->m_Index = i;
	My::Size vi = pos + count < m_Rows.size() ? 
		m_Rows[pos + count]->CalcVisibleIndex() : GetVisibleRowCount();
	Range::Insert(vi, &visible[0], visible.size());

}

void List::Remove(My::Size pos, My::Size count)
{
	if (count == 0)
		return;
	assert(pos + count <= m_Rows.size());
	My::Size vfirst, vcount = 0;
	for (My::Size i = pos; i < pos + count; ++i) {
		Row * pRow = m_Rows[i];
		pRow->m_pList = 0;
		pRow->m_Index = -1;
		Container::Remove(pRow);
		if (pRow->m_Visible && vcount++ == 0)
			vfirst = pRow->GetVisibleIndex();
	}
	Range::Remove(vfirst, vcount);
	m_Rows.erase(m_Rows.begin() + pos, m_Rows.begin() + pos + count);
	for (i = pos; i < m_Rows.size(); ++i)
		m_Rows[i]->m_Index = i;
}

void List::Clear()
{
	for (My::Size i = 0; i < m_Rows.size(); ++i) {
		Row * pRow = m_Rows[i];
		pRow->m_pList = 0;
		pRow->m_Index = -1;
		Container::Remove(pRow);
	}
	Range::Clear();
	m_Rows.clear();
	Invalidate();
}

void List::InvalidateSpan(Span s)
{
	CRect r = ClientRect();
	r.top = s.first;
	r.bottom = s.last;
	InvalidateRect(r);
}

void List::SizeChanged()
{ 
	UpdateContentSize(); 
}

void List::SetRowVisible(Row & row, bool on)
{
	assert(row.GetList() == this);
	if (row.m_Visible != on) {
		My::Size index = row.CalcVisibleIndex();
		if (!row.m_Visible)
			Range::Insert(index, &row);
		else
			Range::Remove(index);
		row.m_Visible = on;
	}
}

CRect List::Bounds() const
{
	return CRect(CPoint(0, 0), m_Box.GetContentSize());
}

void List::ProcessEvent(My::Event & e)
{
	if (e.Is<TimerEvent>())
		OnAutoScroll(true);
	Container::ProcessEvent(e);
}

void List::HandlePaint(CDC * pDC, const CRect & r)
{
	Range::UpdateItems();
	CBrush b; b.CreateSysColorBrush(COLOR_BTNFACE);
	pDC->FillRect(&r, &b);
}

Control * List::GetHoverControl(CPoint p)
{
	int index = VisibleRowIndexAt(p.y);
	if (index >= 0 && My::Size(index) < GetVisibleRowCount())
		return &GetVisibleRow(My::Size(index));
	else
		return 0;
}

void List::Paint(CDC * pDC, const CRect & r)
{
	DispatchEvent(WindowBoundsEvent(true));
	Control::Paint(pDC, r);
	ClipRect clip(this, pDC, ClientRect(), !PaintOutside());
	int first = VisibleRowIndexAt(0);
	assert(first != -1);
	int last = VisibleRowIndexAt(ClientHeight());
	assert(last != -1);
	if (My::Size(last) < GetVisibleRowCount())
		last++;
	for (int i = first; i < last; ++i)
		PaintChild(pDC, r, &GetVisibleRow(My::Size(i)));
}

int List::GetOffset() const
{
	return - m_Box.GetOffset().y;
}

void List::OnScroll(CPoint)
{
	Invalidate();
}

My::Size List::GetContentHeight() const
{
	return GetTotalHeight();
}

void List::UpdateContentSize()
{
	m_Box.SetPreferredContentSize(
		CSize(GetContentWidth(), GetContentHeight()));
	Invalidate();
}

int List::VisibleRowIndexAt(int y)
{
	return Range::ItemIndexAt(y);
}

void List::SetAutoScroll(int d)
{
	m_YAutoScroll = d; 
	m_AutoScroll.Enable(d != 0);
//	TRACE1("YAutoScroll = %d\n", m_YAutoScroll);
}

void List::OnAutoScroll(bool mouseMove)
{
	if (m_YAutoScroll != 0)
	{
		m_Box.SetOffset(m_Box.GetOffset() + CPoint(0, m_YAutoScroll));
		if (mouseMove) {
		// Simulate mouse movement:
			CPoint p; ::GetCursorPos(&p);
			My::MouseMoveEvent e(0, p);
			Control * pControl = GetCaptureControl();
			if (pControl == 0)
				pControl = this;
			pControl->ProcessEvent(My::MouseMoveEvent(0, pControl->ScreenToClient(p)));
		}
	}
}

// 916 821 90 49
// 906 391 95 38

} // My